package com.xiyuan.smartutils.template.engine.expression.operator;

import com.xiyuan.smartutils.Classes;
import com.xiyuan.smartutils.Types;
import com.xiyuan.smartutils.Validates;
import com.xiyuan.smartutils.template.engine.ExpressionParser;
import com.xiyuan.smartutils.template.engine.VariableMap;
import com.xiyuan.smartutils.template.engine.exception.ExpressionException;
import com.xiyuan.smartutils.template.engine.expression.Expression;
import com.xiyuan.smartutils.template.engine.expression.primitive._Variable;
import com.xiyuan.smartutils.template.engine.expression.symbol.Symbol;

import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.util.*;

/**
 * 可索引表达式，对应方括号[]：<br>
 * 1、数组下标取值，如array[0]<br>
 * 2、列表坐标取值、如list[0]<br>
 * 3、MAP取值取值、如map["key"]<br>
 * 4、对象属性取值，如object["name"]<br>
 * 5、数组定义["name"]
 *
 * @version v1.0.0 @author lgz 2020年12月8日 新建与整理
 */
@SuppressWarnings("AlibabaAvoidStartWithDollarAndUnderLineNaming")
public class _Indexable extends Operator {
    private static final long serialVersionUID = 1L;
    private ArrayList<Expression> eList;
    
    public _Indexable(String path, int line, int column) {
        super(path, line, column);
        this.eList = new ArrayList<Expression>(1);
    }
    
    public boolean isArr() {
        return !hasTarget();
    }
    
    @Override
    public int getType() {
        return INDEXABLE;
    }
    
    public int subSize() {
        return eList.size();
    }
    
    public Expression getKey() {
        if (!eList.isEmpty()) return eList.get(0);
        return new _Variable(this.getPath(),
                             this.getLine(),
                             this.getColumn(),
                             "_$$" + System.nanoTime());// 随机变量名目的是为了保证返回null
    }
    
    protected Object build(VariableMap variableMap) {
        // 中括号有2种情况
        // 1，[5,a,dd,.....],表示数组
        // 2，aa[12];//表示取值
        if (isArr()) {
            return buildArr(variableMap);// 数组定义
        }
        
        // 对象或者数组取值
        if (eList.size() != 1) {
            throw new ExpressionException(this,
                                          "对象或者数组表达式{" + this + "}，索引格式不正确，索引表达式只能有一个。");
        }
        
        // 构建父对象
        Object obj = target.execute(variableMap);
        Object _key = eList.get(0).execute(variableMap);
        boolean isPrimitiveKey = _key.getClass().isPrimitive();
        String invalidArrKeyMsg = "数组取值表达式 {" + eList.get(0) + "}返回结果不是整型";
        if (obj == null) {return null;}
        // throw new ExpressionException(this, "对象或者数组表达式{" + this + "}，目标对象不允许为null");
        if (obj.getClass().isArray()) {
            if (!Types.isInteger(_key)) {
                throw new ExpressionException(this, invalidArrKeyMsg);
            }
            return Array.get(obj, isPrimitiveKey ? (int) _key : ((Number) _key).intValue());
        }
        else if (obj instanceof List) {
            List<?> list = (List<?>) obj;
            if (!Types.isInteger(_key)) {
                throw new ExpressionException(this, invalidArrKeyMsg);
            }
            
            return list.get(isPrimitiveKey ? (int) _key : ((Number) _key).intValue());
        }
        else if (obj instanceof Collection) {
            Collection<?> list = (Collection<?>) obj;
            if (!Types.isInteger(_key)) {
                throw new ExpressionException(this, invalidArrKeyMsg);
            }
            
            int idx = isPrimitiveKey ? (int) _key : ((Number) _key).intValue();
            int i = 0;
            for (Object object : list) {
                if (i == idx) {return object;}
                i++;
            }
        }
        else if (obj instanceof String) {
            String str = (String) obj;
            if (!Types.isInteger(_key)) {
                throw new ExpressionException(this, invalidArrKeyMsg);
            }
            return str.charAt(((Number) _key).intValue());
        }
       
        else if (obj instanceof Map) {// MAP类型
            Map<?, ?> map = (Map<?, ?>) obj;
            if (isPrimitiveKey) {
                if (Types.isByte(_key) && map.containsKey(((Number) _key).byteValue())) {
                    return map.get(((Number) _key).byteValue());
                }
                else if (Types.isShort(_key) && map.containsKey(((Number) _key).shortValue())) {
                    return map.get(((Number) _key).shortValue());
                }
                else if (Types.isInt(_key) && map.containsKey(((Number) _key).intValue())) {
                    return map.get(((Number) _key).intValue());
                }
                else if (Types.isLong(_key) && map.containsKey(((Number) _key).longValue())) {
                    return map.get(((Number) _key).longValue());
                }
                else if (Types.isFloat(_key) && map.containsKey(((Number) _key).floatValue())) {
                    return map.get(((Number) _key).floatValue());
                }
                else if (Types.isDouble(_key) && map.containsKey(((Number) _key).doubleValue())) {
                    return map.get(((Number) _key).doubleValue());
                }
                else if (Types.isChar(_key) && map.containsKey(((char) _key))) {
                    return map.get(((Number) _key).doubleValue());
                }
                else {return map.get(_key);}
            }
            return map.get(_key);
        }
        else if (_key instanceof String) {// 对象取属性
            
            
            if (!Validates.isJavaNameIdentifier((String) _key)) {
                throw new ExpressionException(this, "属性表达式{" + this + "}，无效属性名");
            }
            
            Field field = getField(obj, (String) _key);
            if (field != null) {return Classes.getFieldValue(obj, field);}
            
            throw new ExpressionException(this, "属性表达式 {" + target + "}结果未找到“" + _key + "”属性");
        }
        
        throw new ExpressionException(this, "可索引表达式{" + this + "}，格式不正确，目标对象不是数组｜列表｜MAP");
    }
    
    private Object buildArr(VariableMap variableMap){
        // 定义成Object 类型数组
        Object[] arrt = new Object[eList.size()];
        for (int i = 0; i < arrt.length; i++) {arrt[i] = eList.get(i).execute(variableMap);}
        
        // 判断如果是同一类型 则将数组穿成指定类型
        Class<?> claz = null;
        boolean isSame = true;
        for (int i = 0; i < arrt.length; i++) {
            if (arrt[i] == null) {
                isSame = false;
                break;
            }
            
            if (claz == null) {
                claz = arrt[i].getClass();
                if (claz.isPrimitive()) {// 如果是基本类型则需要转换成包装类，
                    if (claz == boolean.class) {
                        claz = Boolean.class;
                        arrt[i] = (Boolean) ((boolean)arrt[i]);
                    }
                    else if (claz == byte.class) {
                        claz = Byte.class;
                        arrt[i] = (Byte) arrt[i];
                    }
                    else if (claz == char.class) {
                        claz = Character.class;
                        arrt[i] = (Character) arrt[i];
                    }
                    else if (claz == short.class) {
                        claz = Short.class;
                        arrt[i] = (Short) arrt[i];
                    }
                    else if (claz == int.class) {
                        claz = Integer.class;
                        arrt[i] = (Integer) arrt[i];
                    }
                    else if (claz == long.class) {
                        claz = Long.class;
                        arrt[i] = (Long) arrt[i];
                    }
                    else if (claz == float.class) {
                        claz = Float.class;
                        arrt[i] = (Float) arrt[i];
                    }
                    else if (claz == double.class) {
                        claz = Double.class;
                        arrt[i] = (Double) arrt[i];
                    }
                }
                continue;
            }
            
            if (claz != arrt[i].getClass()) {
                isSame = false;
                break;
            }
        }
        
        if (!isSame) {return arrt;}
        
        // 转换成同一类型的数组
        Object arr = Array.newInstance(claz, arrt.length);
        System.arraycopy(arrt, 0, arr, 0, arrt.length);
        return arr;
    }
    
    @Override
    public _Indexable setLine(int line) {
        super.setLine(line);
        return this;
    }
    
    @Override
    public _Indexable setColumn(int column) {
        super.setColumn(column);
        return this;
    }
    
    @Override
    public String toString() {
        StringBuilder strb = new StringBuilder();
        if (target != null)// 属性取值
        {strb.append(target);}
        
        strb.append("[");
        for (int i = 0; i < eList.size(); i++) {
            if (i != 0) {strb.append(", ");}
            strb.append(eList.get(i));
        }
        strb.append("]");
        
        return strb.toString();
    }
    
    public void addExpression(Expression expression) {
        if (eList.isEmpty() && expression.getType() == Symbol.SYMBOL_COMMA) {
            return;// 过滤第一个逗号
        }
        
        eList.add(expression);
    }
    
    public _Indexable setTarget(Expression target) {
        super.setTarget(target);
        return this;
    }
    
    public boolean hasTarget() {
        return target != null;
    }
    
    public int size() {
        return eList.size();
    }
    
    @Override
    public void parse() throws ExpressionException {
        Expression last = eList.get(eList.size() - 1);
        if (eList.isEmpty() && last.getType() == Symbol.SYMBOL_COMMA) {
            eList.remove(eList.size() - 1);// 过滤最后一个逗号
        }
        
        ExpressionParser.parse_operators(eList);
        int limit = eList.size() - 1;
        for (int i = 0; i <= limit; i++) {
            Expression curr = eList.get(i);
            if (i == limit || curr.getType() == Symbol.SYMBOL_COMMA) {
                if (curr.getType() == Symbol.SYMBOL_COMMA) {
                    eList.remove(i);
                    limit--;
                    i--;
                }
                continue;
            }
            
            Expression next = eList.get(i + 1);
            if (next.getType() != Symbol.SYMBOL_COMMA) {
                throw new ExpressionException(curr, "数组表达式中{" + curr + "}和{" + next + "}之间缺少‘,’");
            }
        }
        eList.trimToSize();
    }
}
